package com.naiterui.ehp.bs.pharmacist.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.naiterui.common.redis.RedisUtil;
import com.naiterui.common.repository.page.PageParamVO;
import com.naiterui.common.repository.page.PageVO;
import com.naiterui.ehp.bp.bo.esign.EsignNoSecretBO;
import com.naiterui.ehp.bp.bo.esign.PreSignResultBO;
import com.naiterui.ehp.bp.bo.esign.RecomSignParamBO;
import com.naiterui.ehp.bp.bo.recommend.param.RecomAuditParamBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.utils.encypt.AESUtil;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bs.pharmacist.entity.Pharmacist;
import com.naiterui.ehp.bs.pharmacist.entity.PharmacistDrMedicationRecomInfo;
import com.naiterui.ehp.bs.pharmacist.entity.PharmacistMedicationRecom;
import com.naiterui.ehp.bs.pharmacist.entity.PharmacistRecomAuditLog;
import com.naiterui.ehp.bs.pharmacist.entity.PharmacistRecommPrescription;
import com.naiterui.ehp.bs.pharmacist.exception.ExceptionCodes;
import com.naiterui.ehp.bs.pharmacist.feign.IB2cFeignClient;
import com.naiterui.ehp.bs.pharmacist.feign.PharmacistEsignFeignClient;
import com.naiterui.ehp.bs.pharmacist.feign.PharmacistRecommendFeignClient;
import com.naiterui.ehp.bs.pharmacist.repository.mysql.DrMedicationRecomInfoRepository;
import com.naiterui.ehp.bs.pharmacist.repository.mysql.MedicationRecomDetailRepository;
import com.naiterui.ehp.bs.pharmacist.repository.mysql.MedicationRecomRepository;
import com.naiterui.ehp.bs.pharmacist.repository.mysql.PharmacistRepository;
import com.naiterui.ehp.bs.pharmacist.repository.mysql.RecomAuditLogRepository;
import com.naiterui.ehp.bs.pharmacist.repository.mysql.RecommPrescriptionRepository;
import com.naiterui.ehp.bs.pharmacist.service.IRecommendService;
import com.naiterui.ehp.bs.pharmacist.utils.Constants;
import com.naiterui.ehp.bs.pharmacist.vo.recommend.AuditParamVO;
import com.naiterui.ehp.bs.pharmacist.vo.recommend.MedicationRecomVO;
import com.naiterui.ehp.bs.pharmacist.vo.recommend.PrescriptionDetailVO;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @Description 处方业务接口实现
 * @Author gongbaoqiang
 * @Date 2019/12/5 1:32 PM
 */
@Slf4j
@Service
public class PharmacistServiceImpl implements IRecommendService {

    @Autowired
    private MedicationRecomRepository recomRepository;

    @Autowired
    private MedicationRecomDetailRepository recomDetailRepository;

    @Autowired
    private DrMedicationRecomInfoRepository recomInfoRepository;

    @Autowired
    private RecommPrescriptionRepository prescriptionRepository;

    @Autowired
    private RecomAuditLogRepository auditLogRepository;

    @Autowired
    private PharmacistEsignFeignClient esignFeignClient;

    @Autowired
    private PharmacistRecommendFeignClient recommendFeignClient;

    @Value("${file.base.prefix}")
    private String basePath;

    @Autowired
    private IB2cFeignClient b2cFeignClient;
    @Autowired
    private PharmacistEsignFeignClient pharmacistEsignFeignClient;

    @Autowired
    private PharmacistRepository pharmacistRepository;

    @Override
    public PageVO<MedicationRecomVO> findAll(Long pharmacistId, Integer status, PageParamVO pageParamVO) throws BusinessException {
        //根据药师id查询被分配的处方
        Page<PharmacistRecomAuditLog> page = null;

        // 待审核
        if (status == MedicationRecomVO.AUDIT_STATUS_PENDING) {
            page = this.auditLogRepository
                .findByPharmacistIdAndStatus(pharmacistId, status, new Date(), pageParamVO.getPageable(Sort.by(Sort.Direction.DESC, "id")));
        }
        // 已通过
        else if (status == MedicationRecomVO.AUDIT_STATUS_PASS) {
            page = this.auditLogRepository
                .findByPharmacistIdAndStatus(pharmacistId, status, pageParamVO.getPageable(Sort.by(Sort.Direction.DESC, "id")));
        }
        // 未通过
        else if (status == MedicationRecomVO.AUDIT_STATUS_REJECT) {
            page = this.auditLogRepository
                .findByPharmacistIdAndStatus(pharmacistId, status, pageParamVO.getPageable(Sort.by(Sort.Direction.DESC, "id")));
        }
        // 失效
        else if (status == MedicationRecomVO.AUDIT_STATUS_INVAL) {
            page = this.auditLogRepository
                .findByPharmacistIdAndStatus(pharmacistId, status, pageParamVO.getPageable(Sort.by(Sort.Direction.DESC, "id")));
        }
        // 我审核的(审核通过+审核不通过的)
        else if (status == MedicationRecomVO.AUDIT_STATUS_MY) {
            page = this.auditLogRepository.findByPharmacistIdAndStatusIn(pharmacistId,
                Arrays.asList(MedicationRecomVO.AUDIT_STATUS_PASS, MedicationRecomVO.AUDIT_STATUS_REJECT),
                pageParamVO.getPageable(Sort.by(Sort.Direction.DESC, "id")));
        }
        // 全部
        else {
            page = this.auditLogRepository
                .findByPharmacistId(pharmacistId, new Date(), pageParamVO.getPageable(Sort.by(Sort.Direction.DESC, "tt.id")));
        }
//        // 全部
//        if (status == MedicationRecomVO.AUDIT_STATUS_ALL) {
//            page = this.auditLogRepository.findByPharmacistId(pharmacistId, new Date(), pageParamVO.getPageable(Sort.by(Sort.Direction.DESC, "tt.id")));
//        }
//        else if (status == MedicationRecomVO.AUDIT_STATUS_PENDING) {
//            page = this.auditLogRepository.findByPharmacistIdAndStatus(pharmacistId, status, new Date(),
//                                                                       pageParamVO.getPageable(Sort.by(Sort.Direction.ASC, "id")));
//        } else {
//            page = this.auditLogRepository.findByPharmacistIdAndStatusGreaterThan(pharmacistId, MedicationRecomVO.AUDIT_STATUS_PENDING,
//                                                                                  pageParamVO.getPageable(Sort.by(Sort.Direction.DESC, "id")));
//        }

        //处理审核数据

        List<MedicationRecomVO> list = new ArrayList<>();
        List<Long> recomIds = new ArrayList<>();
        page.getContent().forEach(s -> {
            recomIds.add(s.getRecomId());
            list.add(MedicationRecomVO.builder()
                .id(s.getId())
                .recomId(s.getRecomId())
                .status(s.getStatus())
                .doctorName(s.getDoctorName())
                .pharmacistName(s.getPharmacistName())
                .build());
        });

        //关联结果中的处方信息，并拼装返回VO
        Iterable<PharmacistMedicationRecom> iterable = this.recomRepository.findAllById(recomIds);
        Map<Long, PharmacistMedicationRecom> map = new HashMap<>();
        iterable.iterator().forEachRemaining(s -> map.put(s.getId(), s));

        list.stream().forEach(s -> {
            PharmacistMedicationRecom recom = map.get(s.getRecomId());
            if (null != recom) {
                s.setId(recom.getId());
                s.setSerialNumber(recom.getSerialNumber());
                s.setCreatedAt(recom.getCreatedAt());
            }
        });

        return PageVO.newBuilder()
            .totalCount((int) page.getTotalElements())
            .pageSize(page.getSize())
            .pageNo(page.getNumber())
            .result(list)
            .build();
    }


    @Override
    public PrescriptionDetailVO findDetailById(Long recomId) throws BusinessException {


        return this.recommendFeignClient.getPrescriptionDetail(recomId);
    }

    @Override
    public PreSignResultBO getUnionId(Long recomId, Long pharmacistId) throws BusinessException {
        /*//获取分布式锁，防止处方重新分配时拿到当前处方——在审核完成后进行释放
        RedisLock redisLock = RedisLock.getInstance(Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + recomId);
        if (!redisLock.lock()) {
            throw new BusinessException(ExceptionCodes.RECOM_RESET_BY_SYSTEM);
        }*/
        // 缓存标记处方已经被当前药师审核
        RedisUtil.valueOps().set(Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + recomId, pharmacistId, 30);

        PreSignResultBO signResultBO = null;
        try {
            PharmacistMedicationRecom recom = this.recomRepository.get(recomId);
            if (recom == null) {
                throw new BusinessException(ExceptionCodes.RECOM_NOT_EXIST);
            }
            if (new Date().after(recom.getExpireAt())) {
                throw new BusinessException(ExceptionCodes.RECOM_AUDIT_EXPIRED);
            }
            PharmacistRecomAuditLog pharmacistRecomAuditLog = this.auditLogRepository.findFirstByRecomIdAndPharmacistId(recomId, pharmacistId);

            //判断该处方是否分配到该药师
            if (pharmacistRecomAuditLog == null) {
                throw new BusinessException(ExceptionCodes.RECOM_PHARMACIST_NOT_PERMISSION);
            }

            if (pharmacistRecomAuditLog.getStatus() != PharmacistRecomAuditLog.AUDIT_STATUS_PENDING) {
                throw new BusinessException(ExceptionCodes.RECOM_AUDIT_PASS_ALREADY);
            }

            //签名校验
            PharmacistDrMedicationRecomInfo recomInfo = this.recomInfoRepository.findFirstByRecommendId(recomId);

            //医生未签名成功，药师无法签名
            if (recomInfo.getDoctorSignStatus() != PharmacistDrMedicationRecomInfo.DOCTOR_SIGN_STATUS_DONE) {
                throw new BusinessException(ExceptionCodes.RECOM_DOCTOR_SIGN_NOT_SUCCESS);
            }

            PharmacistRecommPrescription pharmacistRecommPrescription = this.prescriptionRepository
                .findFirstByRecommendIdAndType(recomId, PharmacistRecommPrescription.TYPE_DOCTOR_SIGN);

            //医生签名文件不存在，药师无法进行签名
            if (pharmacistRecommPrescription == null) {
                throw new BusinessException(ExceptionCodes.RECOM_DOCTOR_SIGN_FILE_NOT_EXIST);
            }
            // 是否免签
            EsignNoSecretBO noSecretInfo = esignFeignClient.getNoSecretInfo(pharmacistId);
            Integer requestFrom =
                noSecretInfo.getNoSecret() ? RecomSignParamBO.REQUEST_FROM_SYSTEM : RecomSignParamBO.REQUEST_FROM_APP;
            signResultBO = this.esignFeignClient.getUnionId(RecomSignParamBO.builder().bizSn(recom.getSerialNumber())
                .createdAt(recom.getCreatedAt()).patientName(recomInfo.getPatientName())
                .patientAge(recomInfo.getPatientAge() + "").patientGender(recomInfo.getPatientGender())
                .pdfPath(this.basePath + pharmacistRecommPrescription.getPrescriptionPhotoUrl())
                .signUserId(pharmacistId).requestFrom(requestFrom).build());
        } catch (BusinessException e) {
            // redisLock.unlock();
            throw e;
        } finally {
            RedisUtil.keyOps().delete(Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + recomId);
        }

        return signResultBO;
    }

    @Override
    public void sign(Long pharmacistId, Long recomId, String uniqueId, String pin) throws BusinessException {
        // 查询处方是否签名
        PharmacistDrMedicationRecomInfo recomInfo = this.recomInfoRepository.findFirstByRecommendId(recomId);
        PharmacistMedicationRecom recom = this.recomRepository.get(recomId);
        if (recomInfo == null || recom == null) {
            log.warn("药师签名-处方数据缺失 recomId:{}", recomId);
            throw new BusinessException(ExceptionCodes.RECOM_NOT_EXIST);
        }

        if (recomInfo.getDoctorSignStatus() != PharmacistDrMedicationRecomInfo.DOCTOR_SIGN_STATUS_DONE) {
            log.info("药师签名-处方医师未进行电子签名,药师跳过电子签名 recomId:{}", recomId);
            return;
        }
        // 是否免签
        EsignNoSecretBO noSecretInfo = esignFeignClient.getNoSecretInfo(pharmacistId);
        Integer requestFrom = null;
        if (noSecretInfo.getNoSecret()) {
            requestFrom = RecomSignParamBO.REQUEST_FROM_SYSTEM;
            pin = StringUtils.isBlank(noSecretInfo.getPin()) ? null
                : AESUtil.decrypt(CommonConstant.ESIGN_NO_SECRET_KEY, noSecretInfo.getPin());
        }
        // 查询医师签名pdf文件
        String pdfPath = this.getDoctorSignFile(recomId);
        // 药师签署
        RecomSignParamBO recomPreSignVO = RecomSignParamBO.builder().signUserId(pharmacistId).pdfPath(pdfPath)
            .bizSn(recom.getSerialNumber()).createdAt(recom.getCreatedAt()).patientName(recomInfo.getPatientName())
            .patientGender(recomInfo.getPatientGender())
            .patientAge(recomInfo.getPatientAge() + recomInfo.getPatientAgeUnit()).uniqueId(uniqueId).pin(pin)
            .requestFrom(requestFrom).build();
        try {
            this.esignFeignClient.sign(recomPreSignVO);
        } catch (BusinessException e) {
            log.warn("处方审核-药师电子签名异常 recomId:{}, msg:{}", recomId, e.getMessage());
            throw new BusinessException(ExceptionCodes.RECOM_PHARMACIST_SING_FAILED, e.getMessage());
        }
    }

    @Override
    public void audit(AuditParamVO paramVO, Long pharmacistId) throws BusinessException {
        if (paramVO.getStatus() == AuditParamVO.STATUS_AUDIT_NOT_PASS) {
            if (StringUtils.isBlank(paramVO.getRemark())) {
                throw new BusinessException(ExceptionCodes.AUDIT_REMARK_NOT_EMPTY);
            }
        }
        String opeKey = Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + paramVO.getRecomId() + "_" + pharmacistId;
        if (RedisUtil.valueOps().setNx(opeKey, opeKey) <= 0) {
            //只记录日志不抛出异常
            //throw new BusinessException(ExceptionCodes.RECOM_RESET_BY_SYSTEM);
            log.info("处方药师审核 同处方、药师已在处理；recomId={},pharmacistId={}", paramVO.getRecomId(), pharmacistId);
            return;
        }
        RedisUtil.keyOps().expire(opeKey, 10);
        try {
            RecomAuditParamBO bo = new RecomAuditParamBO();
            bo.setRecomId(paramVO.getRecomId());
            bo.setStatus(paramVO.getStatus());
            bo.setRemark(paramVO.getRemark());
            bo.setPharmacistId(pharmacistId);
            bo.setOperator(pharmacistId + "");
            //调用处方服务审核
            this.recommendFeignClient.audit(bo);
        } catch (BusinessException e) {
            throw e;
        } finally {
            /*//获取分布式锁，防止处方重新分配时拿到当前处方——在审核完成后进行释放
            RedisLock redisLock = RedisLock.getInstance(Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + paramVO.getRecomId());
            redisLock.unlock();*/
            RedisUtil.keyOps().delete(Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + paramVO.getRecomId());
            RedisUtil.keyOps().delete(opeKey);
        }

    }

    @Async
    @Override
    public void autoAudit(Long recomId) throws BusinessException {
        String opeKey = null;
        try {
            // 随机获取审核药师
            Set<String> pharmacistIdSet = RedisUtil.setOps().smembers(CommonConstant.AUTO_AUDIT_PHARMACIST_KEY);
            if (CollUtil.isEmpty(pharmacistIdSet)) {
                log.info("系统自动审核处方--没有设置审核药师， recomId: {}", recomId);
                return;
            }
            int randomInt = RandomUtil.randomInt(0, pharmacistIdSet.size());
            List<String> pharmacistIds = new ArrayList<>(pharmacistIdSet);
            Long pharmacistId = Long.parseLong(pharmacistIds.get(randomInt));
            opeKey = Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + recomId + "_" + pharmacistId;
            if (RedisUtil.valueOps().setNx(opeKey, opeKey) <= 0) {
                //只记录日志不抛出异常
                //throw new BusinessException(ExceptionCodes.RECOM_RESET_BY_SYSTEM);
                log.info("处方药师审核 同处方、药师已在处理；recomId={},pharmacistId={}", recomId, pharmacistId);
                return;
            }

            Pharmacist pharmacist = pharmacistRepository.get(pharmacistId);
            PharmacistRecomAuditLog pharmacistRecomAuditLog = auditLogRepository.findFirstByRecomId(recomId);
            pharmacistRecomAuditLog.setAuditType(1);
            pharmacistRecomAuditLog.setPharmacistId(pharmacistId);
            pharmacistRecomAuditLog.setPharmacistName(pharmacist.getName());
            pharmacistRecomAuditLog.setChangedBy("系统");
            pharmacistRecomAuditLog.setChangedAt(new Date());
            auditLogRepository.update(pharmacistRecomAuditLog);

            // 保存操作日志
            recommendFeignClient.recomAddlog(recomId, "系统", 2, pharmacist.getName());
            RedisUtil.keyOps().expire(opeKey, 15);
            // 预签署
            this.getUnionId(recomId, pharmacistId);
            // 开始走免密流程
            EsignNoSecretBO noSecretBO = pharmacistEsignFeignClient.getNoSecretInfo(pharmacistId);
            if (!noSecretBO.getNoSecret()) {
                log.info("系统自动审核处方--医生未开通免密， recomId: {}, pharmacistId: {}", recomId, pharmacistId);
                return;
            }
            // 签名
            this.sign(pharmacistId, recomId, null, AESUtil.decrypt(CommonConstant.ESIGN_NO_SECRET_KEY, noSecretBO.getPin()));
            RecomAuditParamBO bo = new RecomAuditParamBO();
            bo.setRecomId(recomId);
            bo.setStatus(1);
            bo.setPharmacistId(pharmacistId);
            bo.setOperator(pharmacistId + "");
            //调用处方服务审核
            this.recommendFeignClient.audit(bo);
        } catch (Exception e) {
            log.info("系统自动审核处方失败 recomId: {}", recomId);
        } finally {
            /*//获取分布式锁，防止处方重新分配时拿到当前处方——在审核完成后进行释放
            RedisLock redisLock = RedisLock.getInstance(Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + paramVO.getRecomId());
            redisLock.unlock();*/
            RedisUtil.keyOps().delete(CommonConstant.NOT_AUTO_ASSIGN_RECOM + recomId);
            RedisUtil.keyOps().delete(Constants.RECOM_PHARMACIST_AUDIT_LOCK_KEY + recomId);
            if (null != opeKey) {
                RedisUtil.keyOps().delete(opeKey);
            }
        }
    }

    @Override
    public PreSignResultBO preAlSign(Long recomId, Long pharmacistId) throws BusinessException {
        PreSignResultBO signResultBO = null;
        try {
            PharmacistMedicationRecom recom = this.recomRepository.get(recomId);
            if (recom == null) {
                throw new BusinessException(ExceptionCodes.RECOM_NOT_EXIST);
            }
            if (new Date().after(recom.getExpireAt())) {
                throw new BusinessException(ExceptionCodes.RECOM_AUDIT_EXPIRED);
            }
            PharmacistRecomAuditLog pharmacistRecomAuditLog = this.auditLogRepository.findFirstByRecomIdAndAllocatePharmacistId(recomId, pharmacistId);

            //判断该处方是否分配到该调配药师
            if (pharmacistRecomAuditLog == null) {
                throw new BusinessException(ExceptionCodes.RECOM_PHARMACIST_NOT_PERMISSION);
            }

            if (pharmacistRecomAuditLog.getStatus() != PharmacistRecomAuditLog.AUDIT_STATUS_PASS) {
                throw new BusinessException(ExceptionCodes.RECOM_AUDIT_PASS_ALREADY);
            }

            //签名校验
            PharmacistDrMedicationRecomInfo recomInfo = this.recomInfoRepository.findFirstByRecommendId(recomId);

            //医生未签名成功，药师无法签名
            if (recomInfo.getPharmacistSignStatus() != PharmacistDrMedicationRecomInfo.DOCTOR_SIGN_STATUS_DONE) {
                throw new BusinessException(ExceptionCodes.RECOM_DOCTOR_SIGN_NOT_SUCCESS);
            }

            PharmacistRecommPrescription pharmacistRecommPrescription = this.prescriptionRepository
                    .findFirstByRecommendIdAndType(recomId, PharmacistRecommPrescription.TYPE_PHARMACIST_SIGN);

            //医生签名文件不存在，药师无法进行签名
            if (pharmacistRecommPrescription == null) {
                throw new BusinessException(ExceptionCodes.RECOM_DOCTOR_SIGN_FILE_NOT_EXIST);
            }
            // 是否免签
            EsignNoSecretBO noSecretInfo = esignFeignClient.getNoSecretInfo(pharmacistId);
            Integer requestFrom =
                noSecretInfo.getNoSecret() ? RecomSignParamBO.REQUEST_FROM_SYSTEM : RecomSignParamBO.REQUEST_FROM_APP;
            signResultBO = this.esignFeignClient.preSignAlPh(RecomSignParamBO.builder().bizSn(recom.getSerialNumber())
                .createdAt(recom.getCreatedAt()).patientName(recomInfo.getPatientName())
                .patientAge(recomInfo.getPatientAge() + "").patientGender(recomInfo.getPatientGender())
                .pdfPath(this.basePath + pharmacistRecommPrescription.getPrescriptionPhotoUrl())
                .signUserId(pharmacistId).requestFrom(requestFrom).build());
        } catch (BusinessException e) {
            throw e;
        }

        return signResultBO;
    }

    @Override
    public void alSign(Long pharmacistId, Long recomId, String uniqueId, String pin) throws BusinessException {
        // 查询处方是否签名
        PharmacistDrMedicationRecomInfo recomInfo = this.recomInfoRepository.findFirstByRecommendId(recomId);
        PharmacistMedicationRecom recom = this.recomRepository.get(recomId);
        if (recomInfo == null || recom == null) {
            log.warn("调配药师签名-处方数据缺失 recomId:{}", recomId);
            throw new BusinessException(ExceptionCodes.RECOM_NOT_EXIST);
        }

        if (recomInfo.getPharmacistSignStatus() != PharmacistDrMedicationRecomInfo.DOCTOR_SIGN_STATUS_DONE) {
            log.info("调配药师签名-处方医师未进行电子签名,药师跳过电子签名 recomId:{}", recomId);
            return;
        }
        // 查询医师签名pdf文件
        String pdfPath = this.getPharmacistSignFile(recomId);
        // 是否免签
        EsignNoSecretBO noSecretInfo = esignFeignClient.getNoSecretInfo(pharmacistId);
        Integer requestFrom = null;
        if (noSecretInfo.getNoSecret()) {
            requestFrom = RecomSignParamBO.REQUEST_FROM_SYSTEM;
            pin = StringUtils.isBlank(noSecretInfo.getPin()) ? null
                : AESUtil.decrypt(CommonConstant.ESIGN_NO_SECRET_KEY, noSecretInfo.getPin());
        }
        // 药师签署
        RecomSignParamBO recomPreSignVO = RecomSignParamBO.builder().signUserId(pharmacistId).pdfPath(pdfPath)
            .bizSn(recom.getSerialNumber()).createdAt(recom.getCreatedAt()).patientName(recomInfo.getPatientName())
            .patientGender(recomInfo.getPatientGender())
            .patientAge(recomInfo.getPatientAge() + recomInfo.getPatientAgeUnit()).uniqueId(uniqueId).pin(pin)
            .requestFrom(requestFrom).build();
        try {
            this.esignFeignClient.signAlPh(recomPreSignVO);
        } catch (BusinessException e) {
            log.warn("调配处方审核-药师电子签名异常 recomId:{}, msg:{}", recomId, e.getMessage());
            throw new BusinessException(ExceptionCodes.RECOM_PHARMACIST_SING_FAILED, e.getMessage());
        }
    }

    @Async
    @Override
    public void allocateAudit(Long recomId) {
        try {
            // 随机获取调配审核药师
            Set<String> pharmacistIdSet = RedisUtil.setOps().smembers(CommonConstant.ALLOCATE_AUDIT_PHARMACIST_KEY);
            if (CollUtil.isEmpty(pharmacistIdSet)) {
                log.info("处方调配药师签署--没有设置审核药师， recomId: {}", recomId);
                return;
            }
            int randomInt = RandomUtil.randomInt(0, pharmacistIdSet.size());
            List<String> pharmacistIds = new ArrayList<>(pharmacistIdSet);
            Long pharmacistId = Long.parseLong(pharmacistIds.get(randomInt));
            Pharmacist pharmacist = pharmacistRepository.get(pharmacistId);
            PharmacistRecomAuditLog pharmacistRecomAuditLog = auditLogRepository.findFirstByRecomId(recomId);
            pharmacistRecomAuditLog.setAllocatePharmacistId(pharmacistId);
            pharmacistRecomAuditLog.setAllocatePharmacistName(pharmacist.getName());
            pharmacistRecomAuditLog.setChangedBy("系统");
            pharmacistRecomAuditLog.setChangedAt(new Date());
            auditLogRepository.update(pharmacistRecomAuditLog);
            // 预签署
            this.preAlSign(recomId, pharmacistId);
            // 开始走免密流程
            EsignNoSecretBO noSecretBO = pharmacistEsignFeignClient.getNoSecretInfo(pharmacistId);
            if (!noSecretBO.getNoSecret()) {
                log.info("处方调配药师签署--医生未开通免密， recomId: {}, pharmacistId: {}", recomId, pharmacistId);
                return;
            }
            // 签名
            this.alSign(pharmacistId, recomId, null, null);
            // 保存操作日志
            recommendFeignClient.recomAddlog(recomId, "系统", 6, pharmacist.getName());
            PharmacistDrMedicationRecomInfo recom = recomInfoRepository.findFirstByRecommendId(recomId);
            recom.setAllocatePharmacistSignStatus(1);
            recom.setAllocatePharmacistId(pharmacistId);
            recom.setAllocatePharmacistName(pharmacist.getName());
            recom.setAllocatePharmacistSignTime(new Date());
            recom.setChangedBy("系统");
            recom.setChangedAt(new Date());
            recomInfoRepository.update(recom);
            PharmacistRecomAuditLog recomAuditLog = auditLogRepository.findFirstByRecomId(recomId);
            recomAuditLog.setAllocateAuditStatus(1);
            recomAuditLog.setAllocatePharmacistId(pharmacistId);
            recomAuditLog.setAllocatePharmacistName(pharmacist.getName());
            recomAuditLog.setChangedBy("系统");
            recomAuditLog.setChangedAt(new Date());
            auditLogRepository.update(recomAuditLog);
        } catch (Exception e) {
            log.info("处方调配药师签署时出现异常，recomId: {}， e：{}", recomId, e.getMessage());
        }
    }

    @Override
    public void handleAuditPharmacist(Long pharmacistId, Integer type, Integer operate) {
        // 自动审核药师
        if (1 == type) {
            // 新增自动审核药师
            if (1 == operate) {
                RedisUtil.setOps().sadd(CommonConstant.AUTO_AUDIT_PHARMACIST_KEY, pharmacistId + "");
            }
            // 移除自动审核药师
            else if (2 == operate) {
                RedisUtil.setOps().srem(CommonConstant.AUTO_AUDIT_PHARMACIST_KEY, pharmacistId + "");
            }
        }
        // 调配审核药师
        else if (2 == type) {
            // 新增调配审核药师
            if (1 == operate) {
                RedisUtil.setOps().sadd(CommonConstant.ALLOCATE_AUDIT_PHARMACIST_KEY, pharmacistId + "");
            }
            // 移除调配审核药师
            else if (2 == operate) {
                RedisUtil.setOps().srem(CommonConstant.ALLOCATE_AUDIT_PHARMACIST_KEY, pharmacistId + "");
            }
        }
    }

    @Override
    public Set<String> getAuditPharmacist(Integer type) {
        // 自动审核药师
        if (1 == type) {
            return RedisUtil.setOps().smembers(CommonConstant.AUTO_AUDIT_PHARMACIST_KEY);
        }
        // 调配审核药师
        return RedisUtil.setOps().smembers(CommonConstant.ALLOCATE_AUDIT_PHARMACIST_KEY);
    }

    private String getDoctorSignFile(Long recomId) throws BusinessException {
        // 查询医师签名pdf文件
        PharmacistRecommPrescription recomPrescription = this.prescriptionRepository.findFirstByRecommendIdAndType(recomId,
            PharmacistRecommPrescription.TYPE_DOCTOR_SIGN);
        if (recomPrescription == null) {
            log.warn("医师签名文件不存在，无法完成药师签名 recomId:{}", recomId);
            throw new BusinessException(ExceptionCodes.ESIGN_DOWNLOAD_FILE_FAILED, "医师签名文件不存在，无法完成药师签名");
        }
        String basePath = ConfigUtil.getString("sys.static.resources.base.path");
        log.info("药师签名获取医师签名文件：basePath={}, pdfPath={}", basePath, recomPrescription.getPrescriptionPhotoUrl());
        return basePath + recomPrescription.getPrescriptionPhotoUrl();
    }

    /**
     * 获取药师签名文件
     *
     * @param recomId 处方id
     * @return
     * @throws BusinessException
     */
    private String getPharmacistSignFile(Long recomId) throws BusinessException {
        // 查询医师签名pdf文件
        PharmacistRecommPrescription recomPrescription = this.prescriptionRepository.findFirstByRecommendIdAndType(recomId,
                PharmacistRecommPrescription.TYPE_PHARMACIST_SIGN);
        if (recomPrescription == null) {
            log.warn("药师签名文件不存在，无法完成调剂药师签名 recomId:{}", recomId);
            throw new BusinessException(ExceptionCodes.ESIGN_DOWNLOAD_FILE_FAILED, "药师签名文件不存在，无法完成调剂药师签名");
        }
        String basePath = ConfigUtil.getString("sys.static.resources.base.path");
        log.info("调剂药师签名获取药师签名文件：basePath={}, pdfPath={}", basePath, recomPrescription.getPrescriptionPhotoUrl());
        return basePath + recomPrescription.getPrescriptionPhotoUrl();
    }
}
